<template>
    <!--最外画布-->
    <div id="designer_layout"
         @mousedown="startDrag"
         @touchstart="startDrag1"
         @touchmove="doDrag1"
         @touchend="endDrag1"
         @wheel="handleWheel"
         :style="{
                 cursor: dragging ? 'grabbing' : 'default',
                 backgroundColor: designerBgColor,
                 transition: 'background-color 0.3s ease'
             }"
         :class="
                [ defaultLayout.backgroundGrid ? 'background-grid':'',]
             "
    >
        <!--      搜索框      -->
        <div style="background: transparent; position:relative;z-index: 100"
             :style="{
                display: defaultLayout.searchInput ? 'display' : 'none'
            }"
        >
            <div class="search-container">
                <input type="text"
                       style="position: relative"
                       v-model="searchz.name"
                       class="search-input"
                       placeholder="请输入搜索内容"
                       id="searchInput"
                       @input="checkInputValue"
                       @keypress.enter="searchNameFunc"
                       @mousedown="handleInputMouseDown"
                       @mouseup="handleInputMouseUp"
                >
                <button class="clear-button" id="clearButton"
                        @click="clearSearchInput"
                        :class="{ 'visible': searchz.showClearButton }"
                >
                    <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <line x1="18" y1="6" x2="6" y2="18"></line>
                        <line x1="6" y1="6" x2="18" y2="18"></line>
                    </svg>
                </button>
                <button class="search-button" id="searchButton"
                        @click="searchNameFunc"
                >
                    <svg class="search-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <circle cx="11" cy="11" r="8"></circle>
                        <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
                    </svg>
                </button>


            </div>
            <div @click="toggleColorPanel"
                 @mousedown.stop @touchstart.stop  class="color-toggle-btn"
                 :class="{
                            'color-toggle-btn-active': isPanelVisible,
                            'color-toggle-btn-unactive': !isPanelVisible
                          }"
                 :style="{
                    display: defaultLayout.toolPanel ? 'display' : 'none'
            }"
            ></div>
        </div>

        <!--   颜色控制面板   -->
        <div class="color-panel"
             @mousedown.stop @touchstart.stop
             :class="{ 'panel-visible': isPanelVisible }"
        >
            <h3>颜色设置</h3>

            <!-- 设计器背景颜色设置 -->
            <div class="color-control">
                <label>选择背景色</label>
                <div class="color-input-group">
                    <input
                        type="color"
                        :value="designerBgColor"
                        @input="handleDesignerBgChange($event.target.value)"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                    <input
                        type="text"
                        :value="designerBgColor"
                        @input="handleDesignerBgTextChange($event.target.value)"
                        placeholder="#ffffff"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                </div>
            </div>

            <!-- 画布字体颜色设置 -->
            <div class="color-control">
                <label>选择字体色</label>
                <div class="color-input-group">
                    <input
                        type="color"
                        :value="canvasTextColor"
                        @input="handleCanvasTextChange($event.target.value)"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                    <input
                        type="text"
                        :value="canvasTextColor"
                        @input="handleCanvasTextTextChange($event.target.value)"
                        placeholder="#000000"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                </div>
            </div>
            <div class="color-control">
                <label>选择线条颜色</label>
                <div class="color-input-group">
                    <input
                        type="color"
                        :value="canvasLineColor"
                        @input="handleCanvasLineChange($event.target.value)"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                    <input
                        type="text"
                        :value="canvasLineColor"
                        @input="handleCanvasLineChange($event.target.value)"
                        placeholder="#000000"
                        @mousedown="handleInputMouseDown"
                        @mouseup="handleInputMouseUp"
                    >
                </div>
            </div>
            <!-- 自动颜色反转 -->
            <div class="auto-invert-control">
                <label>
                    <input
                        type="checkbox"
                        v-model="autoInvert"
                        @change="handleAutoInvertChange"
                    >
                    自动根据背景调整字体颜色
                </label>
            </div>
            <!-- 背景网格 -->
            <div class="auto-invert-control">
                <label>
                    <input
                        type="checkbox"
                        v-model="defaultLayout.backgroundGrid"
                        @change="handleSetBackgroundGrid"
                    >
                    显示背景网格
                </label>
            </div>
            <!-- 预设颜色方案 -->
            <div class="preset-themes"  >
                <h4>预设方案</h4>
                <div class="theme-buttons">
                    <button @click="applyTheme('light')">浅色模式</button>
                    <button @click="applyTheme('dark')">深色模式</button>
                    <button @click="applyTheme('highContrast')">高对比度</button>
                    <button @click="applyTheme('chennuan')">温暖橙色</button>

                </div>
            </div>
            <h3>功能设置</h3>
            <div class="auto-invert-control">
                <label>
                    <input
                        type="checkbox"
                        v-model="allExtendHide"
                        @change="handleSetExtendHide"
                    >
                    节点数量大于
                    <input style="width: 30px" type="number" v-model="allExtendHideLength">
                    全部折叠
                </label>
            </div>
        </div>

        <!--    详情面板插槽   -->
        <div class="data_info" style="position: absolute">
            <slot name="data_info"></slot>
        </div>

        <!--    画布      -->
        <div id="canvas_container"
             @mousedown="startDrag"

             :style="{
                color: canvasTextColor,
                transition: 'color 0.3s ease'
            }"
        >
            <!--画布 拖拽移动 大小变动-->
            <div id="designer_canvasz" class="designer_canvasz"
                 :style="{
         top: top + 'px', left: left + 'px' , transform: `scale(${scale})`
    }">

                <!--参考样板，根据样板生成dom-->
                <div id="piWQHMUhFO653613" class="sharp_node"
                     style="left: 200px; top: 10px;z-index: 0; visibility: hidden"
                     :style="{ width:defaultNodeWidth  + 'px', height: defaultNodeHeight + 'px'}"

                >
                    <!--内容-->

                    <!--画线，或画图用的-->
                    <canvas id="myCanvas" :width="defaultNodeWidth " :height="defaultNodeHeight" >
                    </canvas>

                    <!--插槽-->
                    <div class="sharp_node" style="width: 100px; height: 100px;">
                        <div class="slot_data" id="slot_x">
                        </div>
                    </div>

                    <!--角标-->
                    <div class="slot_canvas_angle"
                    >
                        +
                    </div>
                </div>
                <div id="leveDom"></div>
                <!--线-->
                <div id="lineDom"></div>

                <!-- 右键 -->
                <div>
                    <!-- 自定义右键菜单 -->
                    <ul class="custom-menu" id="customMenu" @mousedown.stop @touchstart.stop>
                        <li id="getUp" @click="getNodeUp">查询上级</li>
                        <li id="getInfo" @click="getNodeInfo">查看详情</li>
                    </ul>
                </div>



            </div>
        </div>


    </div>
</template>

<script>

/**
 *
 *  对结构内容
 *      依赖于children 字段
 *
 *  若开启
 *      向上查询上级  依赖于id
 *      向下级查询   依赖于id
 *
 * */
import "../vue3-tree/css/tree.css";
import { h, render } from 'vue';

export default {
    name: "tree-z",

    props:{
        treeData:{
            type: Object,
            default: function () {
                return null;
            }
        },
        layout:{
            type: Object,
        }

    },
    computed: {
        defaultNodeWidth(){
            return this.defaultLayout.defaultNodeStyle.width;
        },

        defaultNodeHeight(){
            return this.defaultLayout.defaultNodeStyle.height;
        },
    },

    data(){
        return {


            /*画布大小*/
            canvasContainer:{
                width: 100,
                height: 100
            },

            /*默认布局参数*/
            defaultLayout:{
                /*默认节点属性*/
                defaultNodeStyle: {
                    width: this.layout?.defaultNodeStyle?.width || 100 ,
                    height: this.layout?.defaultNodeStyle?.height || 100
                },

                /*默认节点布局*/
                defaultNodeLayouts:{
                    nodeDistanceX:  this.layout?.defaultNodeLayouts?.nodeDistanceX || 20,  //节点左右距离
                    nodeDistanceY:  this.layout?.defaultNodeLayouts?.nodeDistanceY || 50,  //节点上下距离
                },

                lineColor: this.layout?.lineColor || "#515151",
                //背景网格
                backgroundGrid: this.layout?.rightMenuBar ?? true,
                //右键菜单栏
                rightMenuBar: this.layout?.rightMenuBar ?? true,
                //搜索框
                searchInput: this.layout?.searchInput ?? true,
                //工具面板
                toolPanel: this.layout?.toolPanel ?? true,
            },
            //层级
            topLevel:0,


            /*移动*/
            dragging: false,
            startX: 0,
            startY: 0,
            top: 0,
            left: 0,
            startTime:0,
            startTouches: [],

            /*缩放*/
            scale: this.layout?.scale ?? 0.76,
            zoomFactor: 0.03, // 每次缩放的增量

            //填充内容属性值
            //fillContentProperty:"name",  //临时值

            /*插槽数据*/
            slotData:{
                slotHtml:"",
                matches:[],
                matchesName:[],
            },
            /*搜索*/
            searchz:{
                name: '',
                oldName: '',
                index:0,
                showClearButton: false,
            },

            /*高亮显示*/
            highlightedElements:new Map(),


            /*自定义颜色*/
            // 颜色面板显示状态
            isPanelVisible: false,
            // 颜色值
            designerBgColor: '#fcfcfc',   //面板背景颜色
            canvasTextColor: '#000000',   //面板字体颜色
            canvasLineColor: '#515151',   //面板线颜色
            // 自动反转开关
            autoInvert: true,


            /*input 滑动时防止触发拖拽*/
            isSelectingText: false, // 标记是否正在进行文本选择


            /*右键点击菜单*/
            currentPointDomNode:{},

            /*一键缩放*/
            allExtendHide:false,
            allExtendHideLength:3
        }
    },
    created() {
        // 在created钩子中绑定节流函数（推荐方式）
        this.throttledColorUpdate = this.throttle(this.handleCanvasLineLineChange, 300);
    },
    mounted() {
        this.initDesignerLayout();
        //this.initTree();
        // 初始化检查
        this.checkInputValue();
    },
    methods:{

        //初始化定位中心
        initDesignerLayout(){
            let dom = document.getElementById("designer_layout");
            this.top =  - dom.clientHeight/2;
        },

        //获取初始化节点及数据
        getTreeDefaultProperty(){
            let nodeWith = this.defaultLayout.defaultNodeStyle.width;           //节点宽度  100
            let nodeHeight = this.defaultLayout.defaultNodeStyle.height;   //节点高度   100
            let offsetX = this.defaultLayout.defaultNodeLayouts.nodeDistanceX;    //节点左右偏移 20
            let offsetY = this.defaultLayout.defaultNodeLayouts.nodeDistanceY;   //节点上下偏移 30
            let rootDom = this.getNodeDomElementById("leveDom");
            let rootLineDom = this.getNodeDomElementById("lineDom");
            //默认参数
            let defaultProperty = {
                rootLineDom: rootLineDom,
                rootDom: rootDom,
                offsetX: offsetX,
                offsetY: offsetY,
                nodeWith: nodeWith,
                nodeHeight: nodeHeight,
                nodeWithAddOffsetX: offsetX + nodeWith,
                nodeHeightAddOffsetY: offsetY + nodeHeight,
            };
            return defaultProperty;
        },

        /**
         * 初始化树
         *
         * */
        initTree(){
            this.initTreeBefore();
            setTimeout(()=>{
                if(this.treeData == null){
                    //console.log("数据为空")
                    return;
                }
                this.initTree0();
                this.initTreeAfter();
            },300);

        },
        initTreeBefore(){
            //清空之前所有的节点
            this.clearAllDom();
        },

        clearAllDom(){
            let lineDom = document.getElementById("lineDom");
            if(lineDom)
                lineDom.innerHTML = '';
            let leveDom = document.getElementById("leveDom");
            if(leveDom)
                leveDom.innerHTML = '';
        },

        initTree0(){
            let defaultProperty = this.getTreeDefaultProperty();
            let offsetM = this.canvasContainer.width / 2 + defaultProperty.offsetX / 2; //画布中间  500 + 一半间隙
            let offsetY = defaultProperty.offsetY;   //节点上下偏移 30

            this.createTree(defaultProperty.rootDom,this.treeData,offsetM,offsetY,defaultProperty);
            this.createTreeLine(defaultProperty.rootLineDom,defaultProperty);
        },
        initTreeAfter(){
            // 点击页面其他地方关闭菜单
            document.addEventListener('click', (e) => {

                if(e.target.id === "designer_layout"){
                    this.hideCustomMenu();
                    const searchInput =  document.getElementById('searchInput');
                    searchInput.blur();
                }
            });
        },

        /**
         *
         * 开始创建按层次遍历相应节点
         *
         * @param rootDom 根节点
         * @param rooData 数据
         * @param offsetStartX  当前节点左右偏移
         * @param offsetStartY 当前节点上下偏移
         * @param defaultProperty 默认参数  节点长宽 上下左右间距
         *
         * */
        createTree(rootDom,rooData,offsetStartX,offsetStartY,defaultProperty){
            let offsetStart = 0; //节点起始偏移

            if(rootDom == null)return;

            //需要单独创建第一层以及root的节点
            let level = 0;
            //创建0层级节点
            let levelDom = this.createNodeLevel(rootDom,level);
            //偏移
            offsetStart = offsetStartX - ((defaultProperty.nodeWithAddOffsetX)/2);  //中间偏移 - 数据长度的一半

            let rootId = "cx";
            this.createNodeDom(levelDom,0,"nodeId_"+level+"_"+rootId+"_0",
                offsetStart,defaultProperty.offsetY,rooData);//临时填充内容
            let pid = rootId+"_0";
            ++level;offsetStartY += defaultProperty.nodeHeightAddOffsetY;


            let stackList = [];
            //第一层节点放入stack
            if(rooData.children != null && rooData.children.length > 0){
                for(let i = 0; i < rooData.children.length;i++){
                    stackList.push({pid:pid, data:rooData.children[i]});
                }
            }
            let levelLength = stackList.length;

            //创建第一层层级节点
            levelDom = this.createNodeLevel(rootDom,level);
            let pDom = this.getNodeDomElementById("nodeId_"+(level-1)+"_"+pid);
            offsetStart = pDom.offsetLeft;
            offsetStart -= stackList.length/2 * (defaultProperty.nodeWithAddOffsetX) - (defaultProperty.nodeWithAddOffsetX);


            while(stackList.length > 0){
                let node = stackList.shift();
                //先获取父类的偏移
                if(pid === node.pid){

                }else{
                    let pDom = this.getNodeDomElementById("nodeId_"+(level-1)+"_"+node.pid);
                    offsetStart = pDom.offsetLeft;
                    offsetStart -= (node.pChildLength)/2 * (defaultProperty.nodeWithAddOffsetX)  - (defaultProperty.nodeWithAddOffsetX);
                    pid = node.pid;
                }

                //创建节点
                const {nextOffsetX,nextChildren} = this.createNodeAndAdjust(levelLength,levelDom,level,node,offsetStart,offsetStartY,defaultProperty);


                if(nextOffsetX){
                    offsetStart += defaultProperty.nodeWithAddOffsetX + nextOffsetX;
                }else{
                    offsetStart += defaultProperty.nodeWithAddOffsetX;
                }


                //执行
                if(nextChildren){
                    stackList.push(...nextChildren);
                }
                //当前层级结束
                if(--levelLength === 0){
                    levelLength = stackList.length;
                    ++level; offsetStartY += (defaultProperty.offsetY + defaultProperty.nodeHeight);
                    //节点创建  层级level div
                    levelDom = this.createNodeLevel(rootDom,level);
                }
            }
        },

        /**
         * 创建节点，并调整相关节点 左右偏移
         *
         * 递归函数
         * @param levelDom documentList  dom节点
         * @param level 节点偏移量
         * @param node 节点偏移量
         * @param defaultProperty {offsetX, 节点左右默认距离
                                       offsetY, 节点上下默认距离
                                       nodeWith, 节点默认宽度
                                       nodeWithAddOffsetX}
         *
         * @param offsetStartX 当前节点左右偏移量
         * @param  offsetStartY 当前节点上下偏移量
         * */
        createNodeAndAdjust(levelLength,levelDom,level,node,offsetStartX,offsetStartY,defaultProperty){

            let nodePid = node.pid;
            //offsetStartX 是当前节点的left

            //根据子节点计算偏移量， 当前所有子节点长度的一半 + 一半节点的长度
            //上一层父节点和当前层之前生成的节点，需要移动
            let offsetStart = ((defaultProperty.nodeWithAddOffsetX) * (node.data.children === null ?  1 : node.data.children.length === 0 ? 1 : node.data.children.length)/2) - (defaultProperty.nodeWithAddOffsetX)/2;

            if(node.data.children != null && node.data.children.length > 0){
                //父节点偏移矫正
                this.autoAdjustParentNode(level,nodePid,offsetStart);
                //当前层节点偏移
                let pDomList = this.getNodeLevel(level);
                if(pDomList){
                    let prevNodeList = pDomList.childNodes;
                    this.multiNodeOffset0(prevNodeList, -offsetStart);
                }
            }

            let randomStringNodeId = this.getRandomString(8) + "_"+levelLength;

            //创建节点时 - 一半节点的长度
            this.createNodeDom(levelDom,nodePid,"nodeId_"+level+"_"+randomStringNodeId,
                offsetStartX - (defaultProperty.nodeWithAddOffsetX)/2,offsetStartY,node.data);//临时填充内容


            let nodeChildList = node.data.children;
            let childArrays = [];
            if(nodeChildList){
                for(let i = 0; i < nodeChildList.length;i++){
                    childArrays.push({pid:randomStringNodeId,pChildLength:nodeChildList.length, data:nodeChildList[i]});
                }
                //当前层之后未生成dom的节点需要偏移量
                return {nextOffsetX:offsetStart, nextChildren:childArrays};
            }
            return {nextOffsetX:null,nextChildren:null};

        },



        /**
         * 当前节点的所有父类节点自动撑开
         * 初步加载树时使用该方法，
         *
         * 递归函数
         * @param level documentList  层级
         * @param currentNodeString  当前节点id
         * @param offsetX 节点偏移量
         * */
        autoAdjustParentNode(level,currentNodeString,offsetX){
            level --;
            if(level === 0)
                return;

            //查找父亲节点左右邻居节点
            let pDom = this.getNodeDomElementById("nodeId_"+level+"_"+currentNodeString);
            let prevNodeList = [],nextNodeList = [];
            let prevNode = pDom.previousElementSibling;
            while (prevNode){
                prevNodeList.push(prevNode);
                prevNode = prevNode.previousElementSibling;
            }
            let nextNode = pDom.nextElementSibling;
            while (nextNode){
                nextNodeList.push(nextNode);
                nextNode = nextNode.nextElementSibling;
            }

            //父亲节点左边 向左偏移  ，右边节点向右偏移   offsetX
            this.multiNodeOffset0(prevNodeList,-offsetX);
            this.multiNodeOffset0(nextNodeList,offsetX);


            //继续向上递归撑开节点
            this.autoAdjustParentNode(level,pDom.getAttribute("pid"),offsetX);
        },

        //在最上方插入节点
        insertUpTreeNode(data){
            this.createUpDom(data);
        },
        createUpDom(data0){
            let currentPointDomNode = this.currentPointDomNode;

            let data = {
                domNode:currentPointDomNode,
                domData: data0
            }

            //初始数据
            let defaultProperty = this.getTreeDefaultProperty();

            //需要单独创建第一层以及root的节点
            let level = data.domNode.level - 1;
            data.domData.level = level;
            this.topLevel = level;

            //创建-1层级节点
            let levelDom = this.createNodeLevel(defaultProperty.rootDom,level);
            let currentNode = this.getNodeDomElementById(data.domNode.nodeId);

            //偏移 0层节点偏移
            let offsetStartX = currentNode.offsetLeft;
            let offsetStartY = currentNode.offsetTop - defaultProperty.offsetY - defaultProperty.nodeHeight;

            //给父节点绑定子数据
            data.domData.children = [];
            data.domData.children.push(data.domNode);

            let rootId = this.getRandomString(8);
            let parentName = "nodeId_"+level+"_"+rootId+"_1";
            this.createNodeDom(levelDom,0,parentName,
                offsetStartX,offsetStartY,data.domData);

            //修改原节点的pid
            const element = document.getElementById(data.domNode.nodeId);
            element.setAttribute("pid", rootId+"_1");

            //创建线
            let lineDom = this.getLineLevelDom(defaultProperty.rootLineDom,level+1);


            this.createLineDom(rootId+"_1",data.domNode.nodeId,lineDom,defaultProperty.nodeWith, defaultProperty.offsetY,
                offsetStartX, offsetStartY + defaultProperty.nodeHeight, 2);

        },

        /**
         *
         * @param nodeId  当前节点id
         * @param data  子集合
         */
        insertTreeNode(nodeId,data) {

            if(nodeId == "nodeId_0_cx_0"){
                //console.log("向上查询节点");
                //查询节点
                return;
            }
            let defaultProperty = this.getTreeDefaultProperty();
            //模拟子节点数据
            if(data.children === undefined || data.children === null || data.children.length === 0){
                //console.log("数据为空")
            }else{
                this.insertChildNodeAndAdjust(nodeId, data.children,defaultProperty);
                this.insertChildNodeTreeLine(nodeId,defaultProperty);

                //当前节点要绑定 显示隐藏节点函数
                let nodeDom = this.getNodeDomElementById(nodeId);
                let slot_canvas_angle = nodeDom.querySelectorAll('.slot_angle')[0];

                slot_canvas_angle.addEventListener('click', () =>{
                    this.extendChildren(nodeId,data);
                });
            }



        },
        /**
         * 创建节点时绑定，  显示隐藏节点函数
         */
        extendChildren(nodeId,data) {
            //console.log(nodeId,data)

            //获取其子节点，子线节点
            let pNode = this.getNodeDomElementById(nodeId);
            //收缩
            if(data.extend){
                this.hiddenOrShowNodeAndChildNode(nodeId,"none");
                this.setNodeAttribute(pNode,"extend","false"); //修改当前节点展开状态
                pNode.querySelector('.slot_angle').textContent = this.getAngle("hidden");
                data.extend = false;
            }else{
                this.setNodeAttribute(pNode,"extend","true"); //修改当前节点展开状态
                this.hiddenOrShowNodeAndChildNode(nodeId,"block");
                pNode.querySelector('.slot_angle').textContent =  this.getAngle("extend");
                data.extend = true;
            }

        },



        /**
         *
         * 隐藏显示节点树
         *
         * 根据所变动节点个数/2 * 一个节点距离进行缩放或展开 父节点的距离
         *
         * @param id  eg: nodeId_4_QxzcwpPi_3
         * @param status 隐藏或显示状态  block,none
         *
         * */
        hiddenOrShowNodeAndChildNode(id,status){

            //节点所变动的长度
            let childLength = this.hiddenOrShowNodeChildFollow(id,0,status);
            let defaultProperty = this.getTreeDefaultProperty();
            //重新计算根据孩子节点多少来计算
            let offsetX = defaultProperty.nodeWithAddOffsetX * childLength/2;
            if(status === "block")offsetX = -offsetX;

            //从当前层往上依次遍历树然后进行调整距离
            this.hiddenOrShowNodeAutoAdjust(id,offsetX);
        },
        /**
         *
         *  隐藏或显示节点，子节点跟随
         *
         *  @param id  父节点id   eg: nodeId_4_QxzcwpPi_3
         *  @param itemNumber  参与变动的节点个数
         *  @param status 隐藏或显示状态  block,none
         *
         *  @return 参与变动的节点个数
         *
         * */
        hiddenOrShowNodeChildFollow(id,itemNumber,status){

            let dom = this.getNodeDomElementById(id);
            let domExtend = this.getNodeAttribute(dom,"extend");

            //判断该节点是否为add 状态， add 状态为没有子节点
            if(domExtend == "add"){
                return itemNumber;
            }
            //当展开当前节点时，如果当前节点为未状态，将不计算子节点数量
            if(status == "block" && domExtend == "false"){
                return itemNumber;
            }
            //收缩时，如果当前节点为未展开状态，将不计算子节点数量
            if(status == "none" && domExtend == "false"){
                return itemNumber;
            }

            let pName = id.split("_");
            //当前节点的子节点
            let childNodes = this.getQuerySelectorAll('[pid='+pName[2]+'_'+pName[3]+']');
            //当前节点与子节点的连线
            let childLineNodes = this.getQuerySelectorAll('[pid_line='+pName[2]+'_'+pName[3]+']');

            if(childNodes.length != 0)--itemNumber;
            childNodes.forEach((item) =>{
                item.style.visibility = status == "none" ? "hidden" : "visible";
                itemNumber = this.hiddenOrShowNodeChildFollow(item.id,itemNumber,status);
            });
            childLineNodes.forEach((item) =>{
                item.style.visibility = status == "none" ? "hidden" : "visible";
            });

            return itemNumber+childNodes.length;
        },
        /**
         *
         *  隐藏或显示节点，节点移动
         *
         *  @param id  节点id   eg: nodeId_4_QxzcwpPi_3
         *  @param offsetX  节点偏移量
         *
         * */
        hiddenOrShowNodeAutoAdjust(id,offsetX){
            let dom = this.getNodeDomElementById(id);
            let pid = this.getNodeAttribute(dom,"pid");

            if(pid == "0"){  //最上层 查询节
                return;
            }
            //当前子节点
            let childNodes = this.getQuerySelectorAll('[pid='+pid+']');

            let ids = id.split("_");
            let domIndex = Number(ids[3]);
            for(let i = 0; i < childNodes.length; i++){
                let nIndex = Number(childNodes[i].id.split("_")[3]);
                if(nIndex > domIndex){
                    this.oneNodeOffset(childNodes[i], offsetX);
                }else if(nIndex < domIndex){
                    this.oneNodeOffset(childNodes[i], -offsetX);
                }
            }
            let level = Number(ids[1]);
            //调整了向上隐藏遍历层数  <= 1 变为 <=0
            if(--level <= 0){  //根节点
                return;
            }
            this.hiddenOrShowNodeAutoAdjust("nodeId_"+level+"_"+pid, offsetX);
        },

        /**
         *
         * 创建树时，插入节点自动撑开
         *
         * 根据所插入节点个数计算偏移调整父节点偏移
         *
         * 递归函数
         * @param pid 父id
         * @param childArrays 数据
         * @param defaultProperty 默认参数
         *
         * */
        insertChildNodeAndAdjust(pid,childArrays,defaultProperty){

            if(childArrays.length <= 0)return;

            let pids = pid.split("_");
            let nodePid = pids[2]+"_"+pids[3];
            let level =  Number(pids[1]) + 1;
            let levelDom = this.createNodeLevel(defaultProperty.rootDom,level);


            let pDom = this.getNodeDomElementById(pid);

            pDom.querySelector('.slot_angle').textContent =  this.getAngle("extend");
            this.setNodeAttribute(pDom,"extend","true");

            //当前父节点偏移
            let offsetStartX = pDom.offsetLeft;
            let offsetStartY = pDom.offsetTop + defaultProperty.nodeHeightAddOffsetY;

            //根据子节点计算偏移量， 当前所有子节点长度的一半 + 一半节点的长度
            //上一层父节点和当前层之前生成的节点，需要移动
            let offsetStart = ((defaultProperty.nodeWithAddOffsetX) * (childArrays.length)/2) - (defaultProperty.nodeWithAddOffsetX)/2;

            if(offsetStart != 0){
                //父节点偏移矫正
                this.insertNodeAutoAdjustOffset(level,nodePid,offsetStart);
                offsetStartX -= offsetStart;
            }

            //开始创建节点
            let randomStringNodeId = "";
            let childLength = childArrays.length + 1;

            //当前父节点pidRange
            for(let i = 0; i < childArrays.length; i++){
                randomStringNodeId = this.getRandomString(8) +"_"+ --childLength;

                this.createNodeDom(levelDom,nodePid,"nodeId_"+level+"_"+randomStringNodeId,offsetStartX,offsetStartY,
                    childArrays[i]);

                //创建节点时 - 一半节点的长度
                offsetStartX += defaultProperty.nodeWithAddOffsetX;
            }
        },
        /**
         *
         * 插入节点时，当前节点的所有父类节点自动撑开
         *
         *
         * 递归函数
         * @param level documentList  dom节点
         * @param currentNodeString 节点偏移量
         * @param offsetX 节点偏移量
         * */
        insertNodeAutoAdjustOffset(level,currentNodeString,offsetX){
            level --;
            if(level === 0)
                return;

            //当前节点
            let pDom = this.getNodeDomElementById("nodeId_"+level+"_"+currentNodeString);
            let adjoinNodes = this.getQuerySelectorAll('[pid='+this.getNodeAttribute(pDom,"pid")+']');
            let pId = Number(pDom.id.split("_")[3]);
            let prevNodeList = [],nextNodeList = [];
            for(let i = 0; i < adjoinNodes.length;i++){
                let cId = Number(adjoinNodes[i].id.split("_")[3]);
                if(pId > cId){
                    nextNodeList.push(adjoinNodes[i]);
                }else if(pId < cId){
                    prevNodeList.push(adjoinNodes[i]);
                }
            }
            //父亲节点左边 向左偏移  ，右边节点向右偏移   offsetX
            this.multiNodeOffset(prevNodeList,-offsetX);
            this.multiNodeOffset(nextNodeList,offsetX);

            //继续向上递归撑开节点
            this.insertNodeAutoAdjustOffset(level,pDom.getAttribute("pid"),offsetX);

        },


        //   节点操作


        /**
         * 单节点偏移，子节点跟随移动
         * @param item documentList  dom节点
         * @param offset 节点偏移量
         *
         * */
        oneNodeOffset(item, offset){
            if(item){
                this.moveDomChildFollow(item, offset,0);
                //item.style.left = (item.offsetLeft + offset) + 'px';
            }

        },

        /**
         * 单节点偏移 （子节点跟随移动）
         * @param dom  dom节点
         * @param offsetX 节点偏移量X
         * @param offsetY 节点偏移量Y
         *
         * */
        moveDomChildFollow(dom,offsetX,offsetY){
            let ids = dom.id.split("_");

            if(offsetX != 0)
                dom.style.left = dom.offsetLeft + offsetX +'px';
            if(offsetY != 0)
                dom.style.top = dom.offsetTop + offsetY +'px';

            //当前节点一变动，就重新绘线
            let pId = this.getNodeAttribute(dom,"pid");
            this.resizeCanvasLine(this.getNodeDomElementById(pId.split("_")[0]+"_"+ids[2]),
                this.getNodeDomElementById("nodeId_"+(Number(ids[1])-1)+"_"+pId),dom);

            //子节点list
            let domList = this.getQuerySelectorAll('[pid='+ids[2]+'_'+ids[3]+']');
            if(domList.length === 0)return;
            domList.forEach((item)=>{
                this.moveDomChildFollow(item,offsetX,offsetY);
            });



        },

        /**
         *
         * 多节点偏移（子节点随父节点移动）
         *
         *
         * @param items documentList  dom节点
         * @param offset 节点偏移量
         *
         * */
        multiNodeOffset(items, offset){
            if(items){
                for(let i = 0;i < items.length ;i++){
                    this.oneNodeOffset(items[i],offset);
                }
            }
        },

        /**
         * 多节点偏移
         * @param items documentList  dom节点
         * @param offset 节点偏移量
         *
         * */
        multiNodeOffset0(items, offset){
            if(items){
                for(let i = 0;i < items.length ;i++){
                    items[i].style.left = (items[i].offsetLeft + offset) + 'px';
                    //this.oneNodeOffset(items[i],offset);
                }
            }
        },

        /**
         *  创建node节点  并填充数据
         *
         *     <div class="sharp_node_line">
         *         <div class = "slot_data">data</div>
         *         <div class="slot_angle">+</div>
         *     </div>
         *
         *    @param rootDom  document  根节点
         *    @param nodePid  int 父id
         *    @param  nodeId int 节点id
         *    @param  left int  节点left
         *    @param  top int   节点right
         *    @param data  节点内容
         *
         * */
        createNodeDom(rootDom,nodePid,nodeId,left,top,data){
            let {sharp_node,slot_canvas,slot_canvas_angle} = this.createNodeDom0(nodePid,nodeId,left,top,data);
            sharp_node.appendChild(slot_canvas);
            sharp_node.appendChild(slot_canvas_angle);
            rootDom.appendChild(sharp_node);
        },

        createNodeDom0(nodePid,nodeId,left,top,data){
            //创建node
            let sharp_node = document.createElement('div');
            sharp_node.className = "sharp_node";
            if(nodeId == "0")nodeId="root";
            sharp_node.id = nodeId;

            sharp_node.setAttribute("pid",nodePid);
            //调整node属性
            sharp_node.style.left = left+"px";
            sharp_node.style.top = top+"px";

            sharp_node.style.width = this.defaultNodeWidth +"px";
            sharp_node.style.height = this.defaultNodeHeight +'px';


            //创建node中插槽元素
            let slot_canvas = document.createElement('div');
            slot_canvas.className = "slot_data";

            //slot_canvas.textContent = data[this.fillContentProperty];
            // let html = this.slotData.slotHtml;
            // let matches = this.slotData.matches;
            // let matchesName = this.slotData.matchesName;
            // matches.forEach((item,index)=>{
            //     html = html.replace(item,data[matchesName[index]]);
            // });
            // slot_canvas.innerHTML = html;

            // 渲染插槽内容，传递子组件方法
            data.nodeId = nodeId;
            data.level = Number(nodeId.split("_")[1]);
            this.renderSlotContent(slot_canvas, data);


            if(data.extend === undefined || data.extend === null)
                data.extend = true;


            //创建node中角标元素
            let slot_canvas_angle = document.createElement('div');
            slot_canvas_angle.className = "slot_angle";

            if(data.children === undefined || data.children === null || data.children.length === 0){
                slot_canvas_angle.textContent = this.getAngle("add");
                this.setNodeAttribute(sharp_node,"extend","add");

                slot_canvas_angle.addEventListener("click",()=>{
                    if(data.children === undefined || data.children === null || data.children.length === 0){
                        this.$emit("click-angle",data);
                    }
                });
            }
            else {
                if(data.extend){
                    slot_canvas_angle.textContent = this.getAngle("extend"); //目前都为extend
                    this.setNodeAttribute(sharp_node,"extend","true");
                }
                else
                    slot_canvas_angle.textContent = this.getAngle("hidden");

                //绑定 展示与隐藏节点函数
                slot_canvas_angle.addEventListener('click', () =>{
                    this.extendChildren(nodeId,data);
                });
            }


            return {sharp_node,slot_canvas,slot_canvas_angle}
        },

        // 向插槽传递子组件的方法和数据
        renderSlotContent(container, item) {
            // 向插槽传递子组件的方法和数据
            // const slotVNode = this.$scopedSlots.default({
            //     item: item,
            //     // 传递子组件的方法
            //     clickNode: this.clickNode
            // });
            //
            // const tempComponent = new (this.constructor.extend({
            //     render: (h) => h('div', slotVNode)
            // }))().$mount();
            //
            // container.appendChild(tempComponent.$el);

            // 1. Vue 3 中 this.$slots.default 直接是作用域插槽函数
            const defaultSlot = this.$slots.default;
            if (!defaultSlot) return;

            // 2. 传递作用域数据（和 Vue 2 一致，但插槽从 $slots 取）
            const slotVNode = defaultSlot({
                item: item,
                clickNode: this.clickNode
            });

            // 3. 创建临时组件并渲染（Vue 3 移除了 $mount() 无参数用法，改用 render 函数）
            const tempVNode = h('div', slotVNode);
            render(tempVNode, container);
        },

        //点击Node
        clickNode(params){
            let node = this.getNodeDomElementById(params.data.nodeId);
            this.currentPointDomNode = params.data;
            //console.log('子组件收到父组件传递的数据:', params);
            //console.log('node:', node);

            //加载右键菜单
            if(this.defaultLayout.rightMenuBar)
                this.addPointRight(node,params.data);
        },



        /**
         *  通过  id 获取某个dom节点
         *
         *  @param name  string
         *
         *  eg: ("container")
         *
         * */
        getNodeDomElementById(name){
            let dom = document.getElementById(name);
            if(dom){
                return dom;
            }
            return null;
        },


        /**
         *  为节点个属性Attribute 赋值
         *  @param name  string
         *
         *  @return  str
         *
         * */
        setNodeAttribute(dom,name,value){
            if(dom)
                return dom.setAttribute(name,value);
            else
                return null;
        },
        /**
         *  获取某个属性Attribute 属性值
         *  @param name  string
         *
         *  @return  str
         *
         * */
        getNodeAttribute(dom,name){
            if(dom)
                return dom.getAttribute(name);
            else
                return null;
        },

        /**
         *  创建层节点，不会重复创建
         *
         *  @param dom   document 根节点
         *  @param level  int     当前层级
         *
         *  eg: (rootDom,1)
         *
         * */
        createNodeLevel(dom,level){

            let node = this.getNodeLevel(level);
            if(node){
                return node;
            }
            let sharp_node = document.createElement('div');
            sharp_node.id = "level_"+level;
            if(level < 0){
                dom.insertBefore(sharp_node, dom.firstChild);
            }else
                dom.appendChild(sharp_node);
            return sharp_node;
        },


        /**
         *
         *  获得连线层节点
         *
         *  line_level_X 节点
         *
         * */
        getLineLevelDom(rootDom,level){
            let node = this.getNodeDomElementById("line_level_"+level);
            if(node){
                return node;
            }else{
                let lineDom = document.createElement('div');
                lineDom.id = "line_level_"+level;
                if(level <= 0)
                    rootDom.insertBefore(lineDom,rootDom.firstChild);
                else
                    rootDom.appendChild(lineDom);
                return lineDom;
            }
        },


        /**
         *  获得dom层节点
         *  level_X
         *
         * */
        getNodeLevel(level){
            let node = this.getNodeDomElementById("level_"+level);
            if(node){
                return node;
            }
            return null;
        },

        /**
         *  根据属性值获取节点list
         *
         *  @return  document list
         *  eg: name = '[pid='+name+']'
         * */
        getQuerySelectorAll(name){
            return document.querySelectorAll(name);
        },

        /**
         * 获取角标
         * */
        getAngle(name){
            if(name === "add")
                return "+";
            else if(name === "hidden")
                return "v";
            else if(name === "extend")
                return "-";
        },



        //----------------创建连线--------------

        /**
         *
         * 重新绘制线
         *
         * 节点的子节点为隐藏状态时，线也为隐藏
         *
         *
         * */
        resizeCanvasLine(lineDom,pDom,cDom){
            //父节点，当前节点
            //extend 当前节点与父节点  都为true才重新画
            let pExtend = this.getNodeAttribute(pDom,"extend");
            //let cExtend = this.getNodeAttribute(cDom,"extend");

            let pLeft = pDom.offsetLeft;
            let pTop = pDom.offsetTop;
            let cLeft = cDom.offsetLeft;
            // let cTop = cDom.offsetTop;
            let defaultProperty = this.getTreeDefaultProperty();
            let defaultHalfNodeWith = (defaultProperty.nodeWithAddOffsetX)/2 - defaultProperty.offsetX/2;

            let lineTop = 0;
            let lineLeft = 0;
            let lineWidth = 0;
            let lineHeight = 0;
            let canvasDirection = 0;

            lineTop = pTop;

            //线宽度
            if(pLeft > cLeft){
                //左边
                lineLeft = cLeft + defaultHalfNodeWith;
                canvasDirection = 0;
                lineWidth = pLeft - cLeft;
            }else{
                //右边
                lineLeft = pLeft + defaultHalfNodeWith;
                canvasDirection = 1;
                lineWidth = cLeft - pLeft;
            }
            //中间
            if(lineWidth === 0){
                lineLeft = cDom.offsetLeft;
                lineWidth = defaultProperty.nodeWith;
                canvasDirection = 2;
            }

            lineTop += defaultProperty.nodeHeight;
            lineHeight = defaultProperty.offsetY;

            lineDom.style.left = lineLeft + "px";
            lineDom.style.top =  lineTop + "px";
            lineDom.style.width = lineWidth + "px";
            lineDom.style.height = lineHeight + "px";

            if(pExtend == "false"){
                lineDom.style.visibility = "hidden";
            }

            let canvasDom = lineDom.firstChild;
            canvasDom.width = lineWidth;
            canvasDom.height = lineHeight;
            this.drawLineX(canvasDom,canvasDirection);

        },

        /**
         *
         * 根据pid 获取子节点，创建之间的连线  (插入节点)
         *
         * */
        insertChildNodeTreeLine(pid){

            let pDom = this.getNodeDomElementById(pid);
            let pids = pid.split("_");
            let childNodes = this.getQuerySelectorAll("[pid="+pids[2]+"_"+pids[3]+"]");


            let pLeft = pDom.offsetLeft;
            let pTop = pDom.offsetTop;
            let cLeft = 0;
            let cTop = 0;
            let defaultProperty = this.getTreeDefaultProperty();
            let defaultHalfNodeWith = (defaultProperty.nodeWithAddOffsetX)/2 - defaultProperty.offsetX/2;

            let lineTop = pTop + defaultProperty.nodeHeight;
            let lineLeft = 0;
            let lineWidth = 0;
            let lineHeight = defaultProperty.offsetY;
            let canvasDirection = 0;

            let lineDom = this.getLineLevelDom(defaultProperty.rootLineDom,Number(pids[1]) + 1);

            for(let i = 0; i < childNodes.length; i++){

                cLeft = childNodes[i].offsetLeft;
                cTop = childNodes[i].offsetTop;

                //线宽度
                if(pLeft > cLeft){
                    //左边
                    lineLeft = cLeft + defaultHalfNodeWith;
                    canvasDirection = 0;
                    lineWidth = pLeft - cLeft;
                }else{
                    //右边
                    lineLeft = pLeft + defaultHalfNodeWith;
                    canvasDirection = 1;
                    lineWidth = cLeft - pLeft;
                }
                //中间
                if(lineWidth === 0){
                    lineLeft = cLeft;
                    lineWidth = defaultProperty.nodeWith;
                    canvasDirection = 2;
                }


                this.createLineDom(pids[2]+"_"+pids[3],childNodes[i].id,lineDom,lineWidth,lineHeight,
                    lineLeft, lineTop, canvasDirection);

            }
        },


        /**
         *
         *  创建树时，创建线
         *  结构为树状态，标准树结构
         *
         *  根据当前节点，获取长宽位置创建连线
         *
         * */
        createTreeLine(rootDom,defaultProperty){
            // while (rootDom.firstChild) {
            //     rootDom.removeChild(rootDom.firstChild);
            // }

            let defaultNodeWith = defaultProperty.nodeWith;            //节点宽度
            let defaultNodeHeight = defaultProperty.nodeHeight;         //节点高度
            let defaultOffsetX = defaultProperty.offsetX;  //节点偏移 左右 20
            let defaultOffsetY = defaultProperty.offsetY;  //节点偏移 上下 150

            let defaultHalfNodeWith = (defaultNodeWith+defaultOffsetX)/2 - defaultOffsetX/2;

            let level = this.topLevel;
            let pid = -1;


            //从上往下依次画线
            while(true){
                ++level;
                let node = this.getNodeLevel(level);
                if(node == null)return;
                let childNodes = node.childNodes;
                if(childNodes.length === 0)return;

                //let lineDom = document.createElement('div');
                //lineDom.id = "line_level_"+level;
                //rootDom.appendChild(lineDom);

                let lineDom = this.getLineLevelDom(rootDom,level);

                if(childNodes){
                    let pDomId = 0;
                    let pDom = null;
                    let pDomLeft = 0;
                    let pDomTop = 0;
                    let canvasWidth = 0;
                    let canvasLeft = 0;
                    let canvasDirection = 0;
                    for(let i = 0; i < childNodes.length; i++){
                        //父id
                        pDomId = childNodes[i].getAttribute("pid");
                        if(pid == pDomId){

                        }else{
                            pid = pDomId;
                            pDom = this.getNodeDomElementById("nodeId_"+(level-1)+"_"+pid);
                            pDomLeft = pDom.offsetLeft;
                            pDomTop = pDom.offsetTop;
                        }
                        //线宽度
                        if(pDomLeft > childNodes[i].offsetLeft){
                            //左边
                            canvasLeft = childNodes[i].offsetLeft + defaultHalfNodeWith;
                            canvasDirection = 0;
                            canvasWidth = pDomLeft - childNodes[i].offsetLeft;
                        }else{
                            //右边
                            canvasLeft = pDomLeft + defaultHalfNodeWith;
                            canvasDirection = 1;
                            canvasWidth = childNodes[i].offsetLeft - pDomLeft;
                        }
                        //中间
                        if(canvasWidth === 0){
                            canvasLeft = childNodes[i].offsetLeft;
                            canvasWidth = defaultNodeWith;
                            canvasDirection = 2;
                        }


                        let sharp_node = this.createLineDom(pDomId,childNodes[i].id,lineDom,canvasWidth,defaultOffsetY,
                            canvasLeft, pDomTop + defaultNodeHeight, canvasDirection);



                        if(pDom){
                            const visibility = window.getComputedStyle(pDom).visibility;
                            if(visibility == "hidden" || pDom.getAttribute("extend") == "false")
                                sharp_node.style.visibility = "hidden";
                        }

                    }
                }
            }
        },

        /**
         *  创建线节点
         *
         *   <div class="sharp_node_line">
         *       <canvas id  = "myCanvas"/>
         *       <div class="slot_canvas"/>
         *   </div>
         *
         *  @param pDomId  父id 后半部分。例： tdUaGdzs_1
         *  @param cDomId  子id 全部。例： nodeId_5bMUAMbXa_1
         *  @param levelDom  document  根节点
         *  @param width int
         *  @param  height int
         *  @param  left int
         *  @param  top int
         *          节点的位置长宽
         *  @param direction int 线的方向(0-从右上角到左下角,1-从左上角到右下角,2中间)
         *
         * */
        createLineDom(pDomId,cDomId,levelDom,width,height,left,top,direction){
            //pDomId  tdUaGdzs_1
            //cDomId  nodeId_5bMUAMbXa_1

            //创建node
            let sharp_node = document.createElement('div');
            sharp_node.className = "sharp_node_line";
            sharp_node.setAttribute("pid_line",pDomId);
            sharp_node.id = pDomId.split("_")[0]+"_"+cDomId.split("_")[2];

            let randomString = this.getRandomString(5);

            //调整node属性
            sharp_node.style.left = left+"px";
            sharp_node.style.top = top+"px";
            sharp_node.style.width = width +"px";
            sharp_node.style.height =  height +'px';

            //创建node中画板元素
            let canvas = document.createElement('canvas');
            canvas.id = "myCanvas"+randomString;
            canvas.width = width;
            canvas.height = height;

            //创建node中插槽元素
            let slot_canvas = document.createElement('div');
            slot_canvas.className = "slot_canvas";


            sharp_node.appendChild(canvas);
            sharp_node.appendChild(slot_canvas);
            levelDom.appendChild(sharp_node);
            this.drawLineX(canvas,direction);

            return sharp_node;
        },



        /**
         *  画线 对角波浪曲线
         *  根据当前dom的宽高进行画线，根据direction调节方向
         *
         *
         *  @param dom  document
         *  @param direction  0 从右上角到左下角   1 左上角到右下角  2 中间
         * */
        drawLineX(dom,direction){
            let ctx = dom.getContext("2d");
            let width = dom.clientWidth,height = dom.clientHeight;
            //清空画板
            ctx.clearRect(0, 0, width,height);


            ctx.beginPath();
            //从右上角到左下角
            if(direction === 0){
                ctx.moveTo(width, 0); // 起始点
                ctx.bezierCurveTo(width, height/2, width/2, height/2, width/2, height/2); // 控制点(20, 100)和(180, 100)，终点(180, 20)
                ctx.bezierCurveTo(0, height/2, 0, height, 0, height);

                //arrowWith
                // ctx.quadraticCurveTo(0, height - arrowWith2 , 0, height - arrowWith2);
                // ctx.moveTo(arrowWith, height); // 起始点
                // ctx.quadraticCurveTo(arrowWith2, height - arrowWith2 , arrowWith2, height-arrowWith2);
            }else if(direction === 1){
                //从左上角到右下角
                ctx.moveTo(0, 0); // 起始点
                ctx.bezierCurveTo(0, height/2, width/2, height/2, width/2, height/2); // 控制点(20, 100)和(180, 100)，终点(180, 20)
                ctx.bezierCurveTo(width/2, height/2, width, height/2, width, height);

                // ctx.quadraticCurveTo(width, height - arrowWith2 , width, height - arrowWith2);
                // ctx.moveTo(width - arrowWith, height); // 起始点
                // ctx.quadraticCurveTo(width - arrowWith2, height - arrowWith2 , width - arrowWith2, height - arrowWith2);

            }else if(direction === 2){
                // 起始点在中间

                ctx.moveTo(width/2, 0); // 起始点
                ctx.bezierCurveTo(width/2, height/2,width/2, height/2, width/2, height);


                // ctx.quadraticCurveTo(width/2, height, width/2 - arrowWith, height - arrowWith2);
                // ctx.moveTo(width/2, height); // 起始点
                // ctx.quadraticCurveTo(width/2, height , width/2 + arrowWith, height - arrowWith2);
            }
            ctx.strokeStyle = this.defaultLayout.lineColor;
            ctx.stroke(); // 描边

        },


        // 处理input的鼠标按下事件
        handleInputMouseDown() {
            this.isSelectingText = true;
            setTimeout(() => {
                this.isSelectingText = false;
            }, 500);
        },

        // 处理input的鼠标释放事件
        handleInputMouseUp() {
            // 延迟重置，确保文本选择完成
            setTimeout(() => {
                this.isSelectingText = false;
            }, 100);
        },


        // 画板移动

        /*滚轮缩放*/
        handleWheel(event) {
            // 阻止滚轮事件默认行为
            if(event.ctrlKey){
                event.preventDefault();
                // 检查滚轮方向
                if (event.deltaY < 0) {
                    // 向上滚动（放大）
                    this.scale += this.zoomFactor;
                } else {
                    // 向下滚动（缩小）
                    this.scale -= this.zoomFactor;
                }
                // 限制缩放级别防止过度缩放
                this.scale = Math.max(0.1, Math.min(this.scale, 2)); // 允许的最小值和最大值

            }
        },

        /*点击拖拽开始*/
        startDrag(e) {
            // 如果正在选择文本，不触发拖拽
            if (this.isSelectingText) return;

            //隐藏右键菜单栏
            this.hideOtherPanel();


            // 兼容鼠标和触摸事件
            if (e.target.id === 'designer_layout' || e.target.closest('#designer_layout')) {
                const point = e.touches ? e.touches[0] : e;
                this.startX = point.clientX;
                this.startY = point.clientY;
                this.startTime = Date.now();
                this.dragging = false;
                this.initialLeft = this.left;  // 保存初始位置
                this.initialTop = this.top;

                // 绑定事件处理函数（使用箭头函数保持this上下文）
                const handleMove = (e) => this.doDrag(e);
                const handleEnd = (e) => this.endDrag(e);

                // 存储事件处理函数以便后续移除
                this.moveHandler = handleMove;
                this.endHandler = handleEnd;

                // 添加事件监听
                document.addEventListener('mousemove', handleMove);
                document.addEventListener('mouseup', handleEnd);
                document.addEventListener('touchmove', handleMove, { passive: false });  // 阻止默认行为
                document.addEventListener('touchend', handleEnd);

                // 阻止事件冒泡和默认行为
                // 改进：只在非输入元素上阻止事件
                const target = e.target.tagName.toLowerCase();
                // 检查目标元素是否为输入相关元素
                const isInputElement = ['input', 'textarea', 'select', 'button'].includes(target);

                if (!isInputElement) {
                    e.stopPropagation();
                    if (e.preventDefault) e.preventDefault();
                }
            }

        },
        startDrag1(e){
            //console.log("startDrag1")

            // 如果正在选择文本，不触发拖拽
            if (this.isSelectingText) return;
            //隐藏其他面板
            this.hideOtherPanel();

            this.dragging = true;
            this.startX = e.touches[0].clientX;
            this.startY = e.touches[0].clientY;

            this.startTouches = [...e.touches]; // 记录触点位置
        },
        doDrag1(e){
            if (this.dragging) {
                //一指拖拽
                if (e.touches.length === 1){
                    const deltaX = e.touches[0].clientX - this.startX;
                    const deltaY = e.touches[0].clientY - this.startY;

                    this.left += deltaX * 1.2;
                    this.top += deltaY * 1.2;
                    this.startX = e.touches[0].clientX;
                    this.startY = e.touches[0].clientY;
                }
                //二指缩放
                if (e.touches.length === 2) { // 当两指同时移动时
                    const endTouches = [...e.touches];
                    const startDistance = this.getDistance(this.startTouches[0], this.startTouches[1]);
                    const endDistance = this.getDistance(endTouches[0], endTouches[1]);

                    const scaleDiff = endDistance / startDistance; // 计算缩放比例差
                    this.scale *= scaleDiff; // 更新缩放比例

                    this.startTouches = [...endTouches]; // 更新开始触点位置
                }



            }
        },
        /*触摸缩放*/
        getDistance(touch1, touch2) {
            const dx = touch1.pageX - touch2.pageX;
            const dy = touch1.pageY - touch2.pageY;
            return Math.sqrt(dx * dx + dy * dy);
        },
        /*点触结束*/
        endDrag1() {
            this.dragging = false;
        },
        /*点触移动*/
        doDrag(e) {
            // 兼容鼠标和触摸事件
            const point = e.touches ? e.touches[0] : e;

            // 计算位移
            const deltaX = point.clientX - this.startX;
            const deltaY = point.clientY - this.startY;

            // 判断是否达到拖拽阈值（5px）
            const dragThreshold = 5;
            if (!this.dragging) {
                if (Math.abs(deltaX) > dragThreshold || Math.abs(deltaY) > dragThreshold) {
                    this.dragging = true;  // 标记为拖拽状态
                } else {
                    return;  // 未达到阈值，不执行拖拽
                }
            }

            // 执行拖拽（使用初始位置计算，避免累积误差）
            this.left = this.initialLeft + deltaX * 1.2;
            this.top = this.initialTop + deltaY * 1.2;

            // 阻止触摸设备上的默认行为（如滚动）
            // 阻止事件冒泡和默认行为
            // 改进：只在非输入元素上阻止事件
            const target = e.target.tagName.toLowerCase();
            // 检查目标元素是否为输入相关元素
            const isInputElement = ['input', 'textarea', 'select', 'button'].includes(target);

            if (!isInputElement) {
                if (e.preventDefault) e.preventDefault();
            }
        },

        /*点触结束*/
        endDrag(e) {
            // 判断是否为点击（未触发拖拽且时间短于300ms）
            //const isClick = !this.dragging && (Date.now() - this.startTime < 300);
            // if (isClick) {
            //     this.handleClick(e);  // 触发点击处理
            // }

            // 重置状态并移除事件监听
            this.dragging = false;
            document.removeEventListener('mousemove', this.moveHandler);
            document.removeEventListener('mouseup', this.endHandler);
            document.removeEventListener('touchmove', this.moveHandler);
            document.removeEventListener('touchend', this.endHandler);

            // 清理引用
            this.moveHandler = null;
            this.endHandler = null;
        },

        //随机字符串

        /**
         *  随机字符串
         *  @Param length
         *
         * */
        getRandomString(length) {
            const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
            let result = '';
            for (let i = 0; i < length; i++) {
                const randomIndex = Math.floor(Math.random() * characters.length);
                result += characters[randomIndex];
            }
            return result;
        },
        /**
         *
         * 搜索功能
         */
        // 清除输入内容
        clearSearchInput() {
            this.searchz.name = '';
            this.searchz.showClearButton = false;
            // 聚焦输入框
            this.$refs.searchInput?.focus();
            //清除高亮
            this.clearHighlightElement();

        },
        // 检查输入内容，控制清除按钮显示/隐藏
        checkInputValue() {
            this.searchz.showClearButton = this.searchz.name.trim() !== '';
        },
        searchNameFunc(){
            //恢复原始位置
            if(this.searchz.name === ""){
                this.searchz.oldName = this.searchz.name ;
                let layout = document.getElementById("designer_canvasz");
                //定位节点
                this.locateNode(layout);
                return;
            }

            const treeFontElements = document.getElementsByClassName('tree-node-name');

            if(treeFontElements.length === 0){
                console.log("未找到节点")
                return;
            }

            //innerText
            const matchedElements = [];
            for (let i = 0; i < treeFontElements.length; i++) {
                const element = treeFontElements[i];
                // 使用 includes() 检查是否包含目标文字（不区分大小写可以用toLowerCase()处理）
                if (element.innerText.includes(this.searchz.name)) {
                    matchedElements.push(element);
                }
            }

            //下一个节点
            if(this.searchz.name == this.searchz.oldName){
                this.searchz.index += 1;
                if(this.searchz.index >= matchedElements.length){
                    this.searchz.index = 0;
                }
            }else{
                this.searchz.index = 0;
                this.searchz.oldName = this.searchz.name;
            }


            //获取外界点
            let targetNode = matchedElements[this.searchz.index].parentElement.parentElement.parentElement;

            //定位节点
            this.locateNode(targetNode);

            //添加样式
            this.highlightElement(targetNode);

        },
        //节点计算偏移移动
        locateNode(node) {
            // 获取容器、画布和节点的位置信息
            let layout = document.getElementById("designer_layout");
            let containerRect = layout.getBoundingClientRect();
            const nodeRect = node.getBoundingClientRect();

            // 计算视图中心坐标（相对于容器）
            const viewportCenterX = containerRect.width / 2;
            const viewportCenterY = containerRect.height / 2;

            // 计算节点当前在视图中的中心点坐标
            const nodeCenterInViewX = nodeRect.left + nodeRect.width / 2 - containerRect.left;
            const nodeCenterInViewY = nodeRect.top + nodeRect.height / 2 - containerRect.top;

            // 计算需要移动的距离，使节点中心到达视图中心
            const deltaX = viewportCenterX - nodeCenterInViewX;
            const deltaY = viewportCenterY - nodeCenterInViewY;

            // 计算新的画布位置
            const targetLeft = this.left + deltaX;
            const targetTop = this.top + deltaY;

            // 平滑移动到目标位置
            this.animateToPosition(targetLeft, targetTop);
        },

        // 平滑动画
        animateToPosition(targetLeft, targetTop) {
            const startLeft = this.left;
            const startTop = this.top;
            const duration = 600;
            const startTime = performance.now();

            const animate = (currentTime) => {
                const progress = Math.min((currentTime - startTime) / duration, 1);
                // 缓动函数：先快后慢
                const ease = 1 - Math.pow(1 - progress, 3);

                this.left = startLeft + (targetLeft - startLeft) * ease;
                this.top = startTop + (targetTop - startTop) * ease;

                if (progress < 1) {
                    requestAnimationFrame(animate);
                }
            };

            requestAnimationFrame(animate);
        },
        //清除搜索高亮

        clearHighlightElement(){
            this.removeAllHighlights();
        },

        /**
         * 搜索高亮
         * @param element
         * @param labelText
         */
        highlightElement(element, labelText = null) {
            if (!element) return;

            this.removeAllHighlights();

            // 添加高亮类
            element.classList.add('dom-highlight');

            // 存储原始状态以便后续恢复
            this.highlightedElements.set(element.id, {
                element,
                // originalPosition,
                // label
            });
        },
        removeAllHighlights() {
            // 转换为数组以避免迭代中修改映射
            Array.from(this.highlightedElements.keys()).forEach(id => {
                this.removeHighlight(id);
            });
        },
        removeHighlight(elementId) {
            const data = this.highlightedElements.get(elementId);
            if (data) {
                // 移除高亮类
                data.element.classList.remove('dom-highlight');

                // 从存储中移除
                this.highlightedElements.delete(elementId);
            }
        },

        /*自定义颜色*/
        // 切换颜色面板显示
        toggleColorPanel() {
            this.isPanelVisible = !this.isPanelVisible;
        },


        // 计算颜色亮度 (用于自动反转)
        getBrightness(color) {
            color = color.replace('#', '');
            const r = parseInt(color.substr(0, 2), 16);
            const g = parseInt(color.substr(2, 2), 16);
            const b = parseInt(color.substr(4, 2), 16);
            // YIQ公式计算亮度
            return (r * 299 + g * 587 + b * 114) / 1000;
        },

        // 处理设计器背景色变化
        handleDesignerBgChange(color) {
            this.designerBgColor = color;

            // 如果开启自动反转，更新文字颜色
            if (this.autoInvert) {
                const brightness = this.getBrightness(color);
                this.canvasTextColor = brightness > 128 ? '#000000' : '#e7e7e7';
                this.canvasLineColor = brightness > 128 ? '#515151' : '#e7e7e7';
                this.handleCanvasLineLineChange(this.canvasLineColor)
            }
        },

        // 处理设计器背景色文本输入
        handleDesignerBgTextChange(color) {
            if (/^#[0-9A-F]{6}$/i.test(color)) {
                this.handleDesignerBgChange(color);
            }
        },

        // 处理画布文字颜色变化
        handleCanvasTextChange(color) {
            this.canvasTextColor = color;
            // 手动修改时关闭自动反转
            this.autoInvert = false;
        },

        // 处理画布文字颜色文本输入
        handleCanvasTextTextChange(color) {
            if (/^#[0-9A-F]{6}$/i.test(color)) {
                this.handleCanvasTextChange(color);
            }
        },



        // 节流函数实现
        throttle(func, delay) {
            let timeoutId = null;
            return function(...args) {
                if (!timeoutId) {
                    timeoutId = setTimeout(() => {
                        func.apply(this, args);
                        timeoutId = null;
                    }, delay);
                }
            };
        },
        handleCanvasLineLineChange(color) {
            this.canvasLineColor = color;
            this.defaultLayout.lineColor = this.canvasLineColor;
            this.changeLineColor();
        },
        // 处理线条颜色
        handleCanvasLineChange(color){
            if (/^#[0-9A-F]{6}$/i.test(color)) {
                this.throttledColorUpdate(color);
            }
        },


        // 自动反转开关变化
        handleAutoInvertChange() {
            if (this.autoInvert) {
                // 重新计算颜色
                const brightness = this.getBrightness(this.designerBgColor);
                this.canvasTextColor = brightness > 128 ? '#000000' : '#e7e7e7';
                this.canvasLineColor = brightness > 128 ? '#515151' : '#e7e7e7';
            }
        },
        handleSetBackgroundGrid(){

        },

        // 应用预设主题
        applyTheme(theme) {
            const themes = {
                light: {
                    bg: '#fcfcfc',
                    text: '#000000',
                    line: '#515151'
                },
                dark: {
                    bg: '#2c3e50',
                    text: '#ffffff',
                    line:'#fcfcfc'
                },
                highContrast: {
                    bg: '#0c0c0c',
                    text: '#ffff00',
                    line:'#ffff00'
                },
                chennuan:{
                    bg: '#fff3e0',
                    text: '#e65100',
                    line:'#e65100'
                }
            };

            this.designerBgColor = themes[theme].bg;
            this.canvasTextColor = themes[theme].text;
            this.throttledColorUpdate(themes[theme].line);
            this.autoInvert = true;
        },


        changeLineColor(){
            let lineDom = document.getElementById("lineDom");
            if(lineDom)
                lineDom.innerHTML = '';
            this.createTreeLine(this.getTreeDefaultProperty().rootLineDom,this.getTreeDefaultProperty());
        },


        /*右键菜单栏*/
        addPointRight(dom,data){
            const treeDefaultProperty = this.getTreeDefaultProperty();
            const customMenu = document.getElementById('customMenu');
            const leveDom = document.getElementById('leveDom');
            const firstDom = leveDom.firstChild.firstChild;
            const getUp =  document.getElementById('getUp');
            if(firstDom.id === data.nodeId){
                getUp.classList.remove("disabled");
            }else{
                getUp.classList.add("disabled");
            }


            // 获取鼠标位置
            const x = dom.offsetLeft + treeDefaultProperty.nodeWith / 2;
            const y = dom.offsetTop + treeDefaultProperty.nodeHeight / 2;
            // 显示自定义菜单
            customMenu.style.display = 'block';
            // 设置菜单位置
            customMenu.style.left = `${x}px`;
            customMenu.style.top = `${y}px`;
        },
        clearGetUp(){
            const getUp =  document.getElementById('getUp');
            getUp.classList.add("disabled");
        },
        fixGetUp(){
            const getUp =  document.getElementById('getUp');
            getUp.classList.remove("disabled");
        },

        getNodeUp(){
            this.$emit("click-node-up");
            this.hideCustomMenu();
        },
        getNodeInfo(){
            this.$emit("click-node-info");
            this.hideCustomMenu();
        },
        //隐藏选择框
        hideCustomMenu(){
            const customMenu = document.getElementById('customMenu');
            customMenu.style.display = 'none';
        },
        hideToggleColorPanel(){
            this.isPanelVisible = false;
        },
        //隐藏其他面板
        hideOtherPanel(){
            this.hideCustomMenu();
            this.hideToggleColorPanel();
        },


        handleSetExtendHide(){
            if(this.allExtendHide)
                this.blyc(this.treeData);
        },
        blyc(data){
            if(data.children == null || data.children.length <= 0)
                return;

            data.children.forEach( e =>{
                this.blyc(e);
            })
            //缩放
            if(this.allExtendHide){
                if(data.children.length > this.allExtendHideLength){
                    if(this.allExtendHide === data.extend)
                        this.extendChildren(data.nodeId,data)
                }
            }
        }

    }
}

</script>

<style scoped>

#designer_layout{
    /*background-color: #83b2e5;*/
     background-color: #2c3e50;
    /*background-color: white;*/
    height: 100%
}

/*搜索按钮样式*/
.search-container {
    position: absolute;
    right: 0;
    width: 170px;
    height: 35px;
    display: flex;
    align-items: center;
}

.search-input {
    position: relative;
    z-index: 101;
    width: 100%;
    height: 100%;
    padding: 0 70px 0 15px; /* 增加右侧内边距容纳清除按钮 */
    border: 1px solid #ddd;
    border-radius: 5px; /* 圆角矩形 */
    font-size: 14px;
    transition: all 0.3s ease;
    outline: none;
    box-sizing: border-box;
    color: black;
}

.search-input::placeholder {
    color: #999;
    transition: color 0.3s ease;
}

.search-input:focus {
    border-color: #4d90fe;
    box-shadow: 0 0 0 2px rgba(77, 144, 254, 0.2);
}

.search-input:focus::placeholder {
    color: #ccc;
}

/* 清除按钮样式 */
.clear-button {
    position: absolute;
    right: 35px; /* 位于搜索按钮左侧 */
    z-index: 102;
    width: 24px;
    height: 24px;
    border: none;
    border-radius: 50%;
    background-color: transparent;
    color: #999;
    cursor: pointer;
    transition: all 0.2s ease;
    display: none; /* 默认隐藏 */
    align-items: center;
    justify-content: center;
}

.clear-button:hover {
    background-color: #f0f0f0;
    color: #333;
}

.clear-button.visible {
    display: flex; /* 显示清除按钮 */
}

/* 搜索按钮样式 */
.search-button {
    position: absolute;
    right: 5px;
    z-index: 102;
    width: 30px;
    height: 30px;
    border: none;
    border-radius: 50%;
    background-color: transparent;
    color: #666;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.search-button:hover {
    background-color: #f0f0f0;
    color: #4d90fe;
}

.search-button:active {
    transform: scale(0.95);
}

.search-icon {
    transition: transform 0.3s ease;
}

.search-button:hover .search-icon {
    transform: scale(1.1);
}



/*自定义颜色*/
/* 颜色调整按钮样式 */
.color-toggle-btn {
    position: absolute;
    right: 5px;
    top: 40px;

    width: 45px;
    height: 45px;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    outline: none;

    border-radius: 10px;
    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
    color: white;
    font-size: 1.5rem;
}
/* 方案1：现代渐变风格 */
.color-toggle-btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
}

.color-toggle-btn.active {
    background: linear-gradient(135deg, #6f83d7 0%, #b282e5 100%);
    transform: scale(0.95);
}
.color-toggle-btn-unactive{
    background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.color-toggle-btn-active{
    background: linear-gradient(135deg, #6f83d7 0%, #b282e5 100%);
    transform: scale(0.95);
}

/* 颜色面板样式 */
.color-panel {
    position: absolute;
    top: 40px;
    right: 55px;
    background-color: white;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    padding: 15px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    width: 280px;
    z-index: 101;
    transform: translateY(-10px);
    opacity: 0;
    pointer-events: none;
    transition: all 0.3s ease;
}

.panel-visible {
    transform: translateY(0);
    opacity: 1;
    pointer-events: all;
}

.color-panel h3 {
    margin-top: 0;
    margin-bottom: 5px;
    font-size: 16px;
    color: #333;
}

/* 颜色控制项样式 */
.color-control {
    margin-bottom: 5px;
}

.color-control label {
    display: block;
    margin-bottom: 3px;
    font-size: 14px;
    color: #666;
}

.color-input-group {
    display: flex;
    gap: 10px;
    align-items: center;
}

.color-input-group input[type="color"] {
    width: 40px;
    height: 40px;
    border: none;
    cursor: pointer;
    padding: 0;

}

.color-input-group input[type="text"] {
    flex: 1;
    padding: 8px 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
    color: black;
}

/* 自动反转选项样式 */
.auto-invert-control {
    margin: 10px 0;
    padding: 10px;
    background-color: #f5f5f5;
    border-radius: 4px;
}

.auto-invert-control label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: #333;
    cursor: pointer;
}

/* 预设主题样式 */
.preset-themes {
    margin-top: 5px;
}

.preset-themes h4 {
    margin-bottom: 5px;
    margin-top: 0;
    font-size: 14px;
    color: #666;
}

.theme-buttons {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.theme-buttons button {
    padding: 6px 12px;
    border: none;
    border-radius: 4px;
    font-size: 12px;
    cursor: pointer;
    transition: background-color 0.2s;
}

.theme-buttons button:nth-child(1) {
    background-color: #f0f0f0;
    color: #333;
}

.theme-buttons button:nth-child(2) {
    background-color: #2c3e50;
    color: white;
}

.theme-buttons button:nth-child(3) {
    background-color: #000000;
    color: #ffff00;
}

/* 画布容器样式 */
#canvas_container {
    padding: 20px;
    min-height: 200px;
}
/* 确保所有子元素继承颜色 */
#canvas_container * {
    //color: inherit !important; /* 强制继承父容器颜色 */
    color: inherit; /* 继承父容器颜色 */
    transition: color 0.3s ease; /* 保持过渡动画 */
}


/*右键*/
.custom-menu {

    position: absolute ;
    width: 130px ;
    background-color: #fff ;
    color: black !important;
    border-radius: 6px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    padding: 8px 0;
    margin: 0;
    list-style: none;
    display: none;
    z-index: 1000;
    font-size: 18px;
    text-align: center;
}

.custom-menu li {
    padding: 4px 16px;
    cursor: pointer;
    transition: background-color 0.2s;
}

.custom-menu li:hover {
    background-color: #f0f0f0;
}

.custom-menu li.separator {
    border-top: 1px solid #eee;
    margin: 2px 0;
    padding: 0;
}

.custom-menu li.disabled {
    color: #ccc !important;
    cursor: not-allowed;
}

.custom-menu li.disabled:hover {
    background-color: transparent;
}


</style>
